package com.spzx.cart.service.impl;

import com.spzx.cart.api.domain.CartInfo;
import com.spzx.cart.service.ICartService;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.vo.SkuPriceVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * ClassName: CartServiceImpl
 * Package: com.spzx.cart.service.impl
 * Description:
 *
 * @Author 咚咚小圆帽
 * @Create 2025/3/27 14:14
 * @Version 1.0
 */
@Service
public class CartServiceImpl implements ICartService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RemoteProductService remoteProductService;

    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        //获取用户id
        Long userId = SecurityContextHolder.getUserId();
        //组装购物车的key键
        String cartKey = getCartKey(userId);

        //创建hash绑定对象，对redis中的hash进行操作
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);

        //这里需要加上toString()，不然会出现类型转换错误
        String hashKey = skuId.toString();
        //购物车中商品数量最多只能是99，不能超过99
        Integer threshold = 99;
        //如果存在，证明商品已经在购物车里面了
        if (hashOps.hasKey(hashKey)) {
            CartInfo cartInfo = hashOps.get(hashKey);
            //购物车中的商品+1-1操作
            int totalCount = cartInfo.getSkuNum() + skuNum;
            //大于99的话默认为99，反之该是多少就是多少件商品
            cartInfo.setSkuNum(totalCount > threshold ? threshold : totalCount);
            cartInfo.setUpdateTime(new Date());
            hashOps.put(hashKey, cartInfo);

        } else {
            //购物车中不存在该商品

            //购物车中不同商品的数量不能超过50个
            Long count = hashOps.size();
            if (++count == 50) {
                throw new ServiceException("商品的种类不能超过" + count + "个种类");
            }

            //封装购物车中的商品信息
            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            //大于99的话默认为99，反之该是多少就是多少件商品
            cartInfo.setSkuNum(skuNum > threshold ? threshold : skuNum);
            cartInfo.setUpdateTime(new Date());
            cartInfo.setCreateTime(new Date());

            //远程调用商品微服务获取商品SKU信息(从缓存中获取)
            R<ProductSku> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
            if (R.FAIL == productSkuResult.getCode()) {
                throw new ServiceException(productSkuResult.getMsg());
            }

            //获取商品数据
            ProductSku productSku = productSkuResult.getData();
            cartInfo.setSkuName(productSku.getSkuName());
            cartInfo.setThumbImg(productSku.getThumbImg());

            //远程调用商品微服务获取实时商品价格
            R<SkuPriceVo> skuPriceResult = remoteProductService.getSkuPrice(skuId, SecurityConstants.INNER);
            if (R.FAIL == skuPriceResult.getCode()) {
                throw new ServiceException(skuPriceResult.getMsg());
            }

            //获取实时商品价格
            SkuPriceVo skuPrice = skuPriceResult.getData();
            cartInfo.setCartPrice(skuPrice.getSalePrice());
            cartInfo.setSkuPrice(skuPrice.getSalePrice());

            //最后将key-value键值对添加进redis中
            hashOps.put(hashKey, cartInfo);

        }
    }

    @Override
    public List<CartInfo> getCartList() {
        //获取用户id
        Long userId = SecurityContextHolder.getUserId();
        //获取用户的购物车列表
        String cartKey = getCartKey(userId);
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cartKey);
        //判断购物车列表是否不为空
        if (cartInfoList != null && cartInfoList.size() > 0) {

            //对添加到购物车列表中的商品按照创建时间做降序排序
            cartInfoList.stream()
                    .sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                    .collect(Collectors.toList());

            //获取商品skuId列表
            //C阿让他Info::getSkuId 是方法引用，等价于cartInfo->cartInfo.getSkuId()
            List<Long> skuIdList = cartInfoList.stream().map(CartInfo::getSkuId).toList();

            //获取商品实时价格列表
            R<List<SkuPriceVo>> skuPriceListResult = remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER);
            if (R.FAIL == skuPriceListResult.getCode()) {
                throw new ServiceException(skuPriceListResult.getMsg());
            }
            //获取商品实时价格列表
            List<SkuPriceVo> skuPriceList = skuPriceListResult.getData();
            //将skuPriceList填充到cartInfoList中
            //将skuPriceList指向流式操作,转换成map集合
            //id作为key，实时价格作为value
            //整个操作的时间复杂度为O(n),是一个比较好的算法
            Map<Long, BigDecimal> skuIdToPriceMap = skuPriceList.stream()
                    .collect(Collectors.toMap(SkuPriceVo::getSkuId, SkuPriceVo::getSalePrice));
            //接着往cartInfoList中设置实时价格
            cartInfoList.forEach(cartInfo -> {
                //根据key(id)获取对应的价格(value)
                cartInfo.setSkuPrice(skuIdToPriceMap.get(cartInfo.getSkuId()));
            });
            return cartInfoList;
        }
        //如果购物车列表为空，同样将cartInfoList返回给前端
        return cartInfoList;
    }

    @Override
    public void deleteCart(Long skuId) {
        //获取当前用户id
        Long userId = SecurityContextHolder.getUserId();
        //在购物车中删除商品
        String cartKey = getCartKey(userId);
        //这两种方式都可以
//        redisTemplate.opsForHash().delete(cartKey, skuId.toString());
        BoundHashOperations hashOps = redisTemplate.boundHashOps(cartKey);
        hashOps.delete(skuId.toString());

    }

    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        Long userId = SecurityContextHolder.getUserId();
        //将isChecked更新到redis中的购物车数据中
        String cartKey = getCartKey(userId);
        BoundHashOperations hashOps = redisTemplate.boundHashOps(cartKey);
        //如果存在，做更新操作
        if (hashOps.hasKey(skuId.toString())) {
            CartInfo cartInfo = (CartInfo) hashOps.get(skuId.toString());
            //更新isChecked
            cartInfo.setIsChecked(isChecked);
            //将更新后的数据重新放入redis中
            hashOps.put(skuId.toString(), cartInfo);
        }

    }

    /**
     * 全选更新操作
     *
     * @param isChecked
     */
    @Override
    public void allCheckCart(Integer isChecked) {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        BoundHashOperations hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        cartInfoList.forEach(cartInfo -> {
            cartInfo.setIsChecked(isChecked);
            hashOps.put(cartInfo.getSkuId().toString(), cartInfo);
        });


    }

    @Override
    public void clearCart() {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        //根据cartKey清除所有数据
        redisTemplate.delete(cartKey);
    }

    @Override
    public List<CartInfo> getCartCheckedList(Long userId) {
        String cartKey = getCartKey(userId);
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cartKey);
        //如果链表非空
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            cartInfoList = cartInfoList.stream()
                    //我们需要拿到isChecked=1的数据
                    .filter(cartInfo -> cartInfo.getIsChecked().intValue() == 1)
                    .collect(Collectors.toList());
        }

        return cartInfoList;
    }

    /**
     * 删除用户购物车列表中选中商品列表
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public Boolean deleteCartCheckedList(Long userId) {
        //获取购物车中选中的数据
        String cartKey = this.getCartKey(userId);
        BoundHashOperations hashOps = redisTemplate.boundHashOps(cartKey);
        //拿到了购物车中选中的数据
        List<CartInfo> cartInfoList = hashOps.values();

        //需要做一个非空判断，加强代码的健壮性
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            //遍历列表，删除购物车中的数据
            for (CartInfo cartInfo : cartInfoList) {
                //表示该商品已经被选中
                if (cartInfo.getIsChecked().intValue() == 1) {
                    //那么就删除该商品
                    //我们是按照<String,String,Object>的方式存储的，
                    //所以需要将id转换为String类型
                    hashOps.delete(cartInfo.getSkuId().toString());
                }
            }
        }

        //如果以上操作没有抛出异常，那么直接返回
        return true;
    }

    /**
     * 更新购物车价格
     *
     * @param userId 用户id
     * @return
     */
    @Override
    public Boolean updateCartPrice(Long userId) {
        //拿到购物车中的商品
        String cartKey = getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        //健壮性判断，判断购物车是否为空
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            //遍历购物车列表，如果商品价格发生了改变，更新商品价格
            for (CartInfo cartInfo : cartInfoList) {
                //我们只改变购物车中选中商品的价格，其他的不用管
                if (cartInfo.getIsChecked().intValue() == 1) {
                    //拿到最新的单个商品的价格
                    R<SkuPriceVo> skuPriceResult = remoteProductService.getSkuPrice(cartInfo.getSkuId(), SecurityConstants.INNER);
                    if (R.FAIL == skuPriceResult.getCode()) {
                        throw new ServiceException(skuPriceResult.getMsg());
                    }
                    //得到实时价格
                    SkuPriceVo skuPriceVo = skuPriceResult.getData();
                    BigDecimal salePrice = skuPriceVo.getSalePrice();
                    //设置实时价格
                    cartInfo.setCartPrice(salePrice);
                    cartInfo.setSkuPrice(salePrice);
                    //将更新之后得到的实时价格存储到购物车中
                    hashOps.put(cartInfo.getSkuId().toString(), cartInfo);
                }
            }
        }
        return true;
    }

    /**
     * 购物车的key
     *
     * @param userId
     * @return
     */
    //为每个不同的用户设置不同的key键，存储在redis中的哈希结构中。
    private String getCartKey(Long userId) {
        return "user:cart:" + userId;
    }

}
